/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2020 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

"use strict";

var app = angular.module("axelor.app");

function useSingleTabOnly() {
  return axelor.device.mobile
    || !!axelor.config['view.single.tab']
    || axelor.config['user.singleTab']
    || +(axelor.config["view.tabs.max"]) === 0
    || +(axelor.config["view.tabs.max"]) === 1;
}

app.factory('NavService', ['$location', 'MenuService', function($location, MenuService) {

  var tabs = [];
  var popups = [];
  var selected = null;

  var VIEW_TYPES = {
    'list' : 'grid',
    'edit' : 'form'
  };

  function findTab(key) {
    return _.find(tabs, function(tab){
      return tab.action == key;
    });
  }

  function findTabTitle(tab) {
    var first;
    if (tab.title) {
      return tab.title;
    }
    first = _.first(tab.views);
    if (first) {
      return first.title || first.name;
    }
    return tab.name || "Unknown";
  }

  function openView(view, options) {
    if (view && (view.type || view.viewType) == 'html') {
      var first = _.first(view.views) || view;
      view.views = [{
        name: first.name,
        resource: first.resource,
        title: first.title,
        type: 'html'
      }];
      if ((view.params||{}).target === "_blank") {
        var url = first.name || first.resource;
        return setTimeout(function () {
          window.open(url);
        });
      }
    }

    var closable = options && options.__tab_closable;
    if (!closable && view.params && view.params.closable !== undefined) {
      closable = view.params.closable;
    }

    view.closable = closable;

    openTab(view, options);
  }

  function openTabByName(name, options) {

    var tab = findTab(name);
    if (tab) {
      return openTab(tab, options);
    }

    return MenuService.action(name).success(function(result){
      if (!result.data) {
        return;
      }
      var view = result.data[0].view;
      view.action = name;
      return openView(view, options);
    });
  }

  function openTabAsPopup(tab, options) {
    popups.push(tab);
  }

  function openTab(tab, options) {

    if (tab && tab.$popupParent) {
      return openTabAsPopup(tab, options);
    }

    options = options || tab.options;

    if (options && options.mode) {
      tab.viewType = VIEW_TYPES[options.mode] || options.mode;
    }

    tab.options = options;
    tab.title = tab.title || findTabTitle(tab);

    if (tab.action && MenuService.updateTabStyle) {
      MenuService.updateTabStyle(tab);
    }

    function __doSelect(found) {

      var lastScope = (selected||{}).$viewScope || {};
      if (lastScope.$locationChangeOff) {
        lastScope.$locationChangeOff();
      }

      found.selected = true;
      selected = found;

      if (options && tab.$viewScope) {
        var view = tab.$viewScope._views[tab.viewType],
          promise = view ? view.deferred.promise : null;
        if (promise) {
          promise.then(function(viewScope) {
            viewScope.setRouteOptions(options);
          });
        }
      }

      setTimeout(function(){
        axelor.$adjustSize();
      });
    }

    function __singleShow(found) {
      tabs.length = 0;
      tabs.push(found);
      return __doSelect(found);
    }

    var found = findTab(tab.action);

    if (useSingleTabOnly()) {

      if (found) {
        return __singleShow(found);
      }

      var last = _.last(tabs);
      if (last) {
        return closeTab(last, function () {
          __singleShow(tab);
        });
      }
      return __singleShow(tab);
    }

    if (!found) {
      found = tab;
      __closeUnusedTabs();
      if (options && options.__tab_prepend) {
        tabs.unshift(tab);
      } else {
        tabs.push(tab);
      }
    }

    _.each(tabs, function(tab) {
      tab.selected = false;
    });

    return __doSelect(found);
  }

  var MAX_TABS;

  function __closeUnusedTabs() {
    if (MAX_TABS === undefined) {
      MAX_TABS = +(axelor.config["view.tabs.max"]) || -1;
    }
    if (MAX_TABS <= 0 || tabs.length < MAX_TABS) {
      return;
    }

    var all = _.filter(tabs, function (tab) {
      return !tab.selected && canCloseTab(tab);
    });
    var doClose = function doClose(tab) {
      var index = _.indexOf(tabs, tab);
      var vs = tab.$viewScope;
      if (vs && vs.isDirty && vs.isDirty()) return;
      if (vs && vs.$details && vs.$details.isDirty && vs.$details.isDirty()) return;
      tabs.splice(index, 1);
    };

    for (var i = 0; i < all.length; i++) {
      doClose(all[i]);
      if (tabs.length === 0) selected = null;
      if (tabs.length < MAX_TABS) break;
    }
  }

  function __closeTab(tab, callback) {

    var all = tab.$popupParent ? popups : tabs;
    var index = _.indexOf(all, tab);

    // remove tab
    all.splice(index, 1);

    if (tabs.length === 0) {
      selected = null;
    }
    if (_.isFunction(callback)) {
      callback();
    }
    if (tab.$popupParent) {
      return;
    }

    if (tab.selected) {
      if (index == tabs.length)
        index -= 1;
      _.each(all, function(tab){
        tab.selected = false;
      });
      var select = all[index];
      if (select) {
        select.selected = true;
        openTab(select);
      } else {
        $location.path('/');
      }
    }
  }

  function canCloseTab(tab) {
    return tab.closable === undefined ? true : tab.closable;
  }

  function closeTab(tab, callback) {
    var viewScope = tab.$viewScope;
    if (viewScope && viewScope.confirmDirty) {
      viewScope.confirmDirty(function(){
        __closeTab(tab, callback);
      });
    } else {
      __closeTab(tab, callback);
    }
  }

  function closeTabs(selection) {
    var all = _.flatten([selection], true);

    function select(tab) {
      if (!tab.selected) {
        tab.selected = true;
        openTab(tab);
      }
    }

    function close(tab, ignore) {
      var at = tabs.indexOf(tab);
      if (at > -1) {
        tabs.splice(at, 1);
      }
      closeTabs(_.difference(selection, [ignore, tab]));
      if (tabs.length === 0) {
        selected = null;
      }
    }

    function doConfirm(tab, viewScope) {
      return viewScope.confirmDirty(function(){
        return close(tab);
      }, function() {
        close(null, tab);
        viewScope.$applyAsync();
      });
    }

    for (var i = 0; i < all.length; i++) {
      var tab = all[i];
      var viewScope = tab.$viewScope;
      if (viewScope && viewScope.confirmDirty) {
        select(tab);
        return doConfirm(tab, viewScope);
      }
      return close(tab);
    }

    if (tabs.indexOf(selected) == -1) {
      selected = null;
    }

    if (selected) {
      return openTab(selected);
    }

    var first = _.first(tabs);
    if (first && !first.selected) {
      return openTab(first);
    }

    axelor.$adjustSize();
  }

  function closeTabOthers(current) {
    var rest = _.filter(tabs, function(tab) {
      return canCloseTab(tab) && tab !== current;
    });
    if (current && !current.selected) {
      current.selected = true;
      openTab(current);
    }
    return closeTabs(rest);
  }

  function closeTabAll() {
    closeTabOthers();
  }

  function reloadTab(current) {
    var viewScope = current.$viewScope;
    if (viewScope) {
      viewScope.$broadcast('on:tab-reload', current);
    }
  }

  function getTabs() {
    return tabs;
  }

  function getPopups() {
    return popups;
  }

  function getSelected() {
    return selected;
  }

  return {
    openTabByName: openTabByName,
    openTab: openTab,
    openView: openView,
    canCloseTab: canCloseTab,
    reloadTab: reloadTab,
    closeTab: closeTab,
    closeTabOthers: closeTabOthers,
    closeTabAll: closeTabAll,
    getTabs: getTabs,
    getPopups: getPopups,
    getSelected: getSelected
  };
}]);

NavCtrl.$inject = ['$scope', '$rootScope', '$location', 'NavService'];
function NavCtrl($scope, $rootScope, $location, NavService) {

  $scope.singleTabOnly = useSingleTabOnly();

  Object.defineProperty($scope, '$location', {
    get: function() {
      return $location;
    }
  });

  Object.defineProperty($scope, 'navTabs', {
    get: function() {
      return NavService.getTabs();
    }
  });

  Object.defineProperty($scope, 'navPopups', {
    get: function() {
      return NavService.getPopups();
    }
  });

  Object.defineProperty($scope, 'selectedTab', {
    get: function() {
      return NavService.getSelected();
    }
  });

  $scope.hasNabPopups = function () {
    return $scope.navPopups && $scope.navPopups.length > 0;
  };

  $scope.menuClick = function(event, record) {

    if (!record.action) {
      return;
    }

    if (axelor.device.small) {
      $("#offcanvas").removeClass("active");
    }

    $scope.openTabByName(record.action);
    $scope.$applyAsync();
  };

  $scope.navClick = function(tab) {
    $scope.openTab(tab);
    $scope.$broadcast("on:nav-click", tab);
  };

  $scope.$on("on:update-route", update);

  function update(event) {

    var tab = $scope.selectedTab,
      scope = event.targetScope;

    if (!tab || !tab.action || scope !== tab.$viewScope || !scope.getRouteOptions) {
      return;
    }
    if (tab.action.indexOf('$act') > -1) {
      return;
    }

    var path = tab.action,
      opts = scope.getRouteOptions(),
      mode = opts.mode,
      args = opts.args;

    path = "/ds/" + path + "/" + mode;
    args = _.filter(args, function(arg) {
      return _.isNumber(args) || arg;
    });

    if (args.length) {
      path += "/" + args.join("/");
    }

    if ($location.$$path !== path) {
      $location.path(path);
      $location.search(opts.query || "");
    }
  }

  $scope.canCloseTab = function(tab) {
    return NavService.canCloseTab(tab);
  };

  $scope.openTab = function(tab, options) {
    return NavService.openTab(tab, options);
  };

  $scope.openTabByName = function(name, options) {
    return NavService.openTabByName(name, options);
  };

  $scope.closeTab = function(tab, callback) {
    var wasSelected = tab.selected;
    if (NavService.canCloseTab(tab)) {
      NavService.closeTab(tab, callback);
      if ($scope.selectedTab && wasSelected) {
        $scope.$broadcast("on:nav-click", $scope.selectedTab);
      }
    }
  };

  $scope.closeTabOthers = function(tab) {
    var wasSelected = tab.selected;
    NavService.closeTabOthers(tab);
    if ($scope.selectedTab === tab && !wasSelected) {
      $scope.$broadcast("on:nav-click", tab);
    }
  };

  $scope.closeTabAll = function() {
    return NavService.closeTabAll();
  };

  $scope.reloadTab = function(tab) {
    return NavService.reloadTab(tab);
  };

  $scope.tabTitle = function(tab) {
    var vs = tab.$viewScope || {};
    if (vs.viewType === "form") {
      return vs.viewTitle || tab.title;
    }
    return tab.title;
  };

  $scope.tabDirty = function(tab) {
    var viewScope = tab.$viewScope;
    if (viewScope && viewScope.isDirty) {
      return viewScope.isDirty();
    }
    return false;
  };

  // expose common methods to $rootScope
  $scope.$root.openTab = $scope.openTab;
  $scope.$root.openTabByName = $scope.openTabByName;

  $scope.$watch('selectedTab.viewType', function tabViewTypeWatch(viewType){
    if (viewType) {
      axelor.$adjustSize();
    }
  });

  $scope.$watch('routePath', function routePathWatch(path) {
    $scope.openHomeTab();
  });

  var confirm = _t('Current changes will be lost.');

  function onbeforeunload(e) {
    var tabs = $scope.navTabs || [];
    for (var i = 0; i < tabs.length; i++) {
      var vs = (tabs[i]||{}).$viewScope;
      if (vs && vs.$$dirty) {
        return confirm;
      }
    }
  }

  $(function () {

     // menu toggle logic
     var menuToggled = false;
     var navigator = axelor.config["user.navigator"];

     if (navigator !== 'hidden') {
       $('#offcanvas-toggle').find('a').click(function (e) {
         var active = ! $("#offcanvas").hasClass('inactive');
         if (active && axelor.device.small) {
           active =  $("#offcanvas").hasClass('active');
         }
           $("#offcanvas").toggleClass("active", !active && axelor.device.small);
           $("#offcanvas").toggleClass("inactive", active && !axelor.device.small);
           if (!axelor.device.mobile) {
             setTimeout(axelor.$adjustSize, 100);
         }
       });
     }

     $("#offcanvas,#offcanvas-toggle").toggleClass("hidden-menu", navigator === "hidden");
     if (navigator === "collapse") {
       $("#offcanvas").addClass("inactive");
     }
     $scope.ajaxStop(function () {
       setTimeout(function () {
         $("#offcanvas,#offcanvas-toggle").removeClass("hidden");
       }, 100);
     }, 100);

     $(window).on('resize', _.debounce(function () {
       $("#offcanvas").removeClass(axelor.device.small ? 'inactive' : 'active');
       setTimeout(axelor.$adjustSize, 100);
     }, 100));

     // confirm dirty
     $(window).on('beforeunload', onbeforeunload);
  });
}

TabCtrl.$inject = ['$scope', '$location', '$routeParams'];
function TabCtrl($scope, $location, $routeParams) {

  var homeAction = axelor.config["user.action"],
    params = _.clone($routeParams),
    search = _.clone($location.$$search);

  var opts = {
    mode: params.mode,
      state: params.state,
      search: search
  };

  if (homeAction === params.resource) {
    _.extend(opts, {
      __tab_prepend: true,
      __tab_closable: false
    });
  }

  if (params.resource) {
    $scope.openTabByName(params.resource, opts);
    }
}

app.controller("NavCtrl", NavCtrl);
app.controller("TabCtrl", TabCtrl);

})();
